package org.example.Proxy;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpReverseProxyList {
    static Log log = LogFactory.get();

    private final int port;
    private final List<ProxyTargetModel> proxyTargetModels;

    public HttpReverseProxyList(int port, List<ProxyTargetModel> proxyTargetModels) {
        this.port = port;
        this.proxyTargetModels = proxyTargetModels;
    }

    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 创建一个ServerBootstrap实例，用于设置服务器参数和初始化Channel
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    // 设置Channel类型为NioServerSocketChannel，表示使用NIO来接收和处理新连接
                    .channel(NioServerSocketChannel.class)
                    // 设置日志处理器，记录日志级别为INFO
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 设置子通道的处理器为HttpReverseProxyInitializer，用于初始化新接入的客户端连接
                    .childHandler(new HttpReverseProxyInitializer(proxyTargetModels));

            Channel ch = b.bind(port).sync().channel();

            System.out.println("HTTP Reverse Proxy started at http://127.0.0.1:" + port + '/');
            // 等待服务器Socket关闭
            ch.closeFuture().sync();
        } finally {
            // 优雅地关闭EventLoopGroup，释放资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    // 主函数，创建并启动HTTP反向代理服务器实例
    public static void main(String[] args) throws Exception {

        List<ProxyTargetModel> list = new ArrayList<>();
        list.add(new ProxyTargetModel("/server.*", "localhost", 8080));
        list.add(new ProxyTargetModel("/ui.*", "localhost", 3000));

        new HttpReverseProxyList(8001, list).start();
    }

    // 定义一个内部类，用于初始化新接入的客户端连接
    private static class HttpReverseProxyInitializer extends ChannelInitializer<SocketChannel> {

        private final List<ProxyTargetModel> proxyTargetModels;

        // 包含后端服务器的主机名和端口
        public HttpReverseProxyInitializer(List<ProxyTargetModel> proxyTargetModels) {
            this.proxyTargetModels = proxyTargetModels;
        }

        // 重写initChannel方法，设置ChannelPipeline中的处理器
        @Override
        protected void initChannel(SocketChannel ch) {
            ChannelPipeline pipeline = ch.pipeline();

            // 添加HTTP请求解码器
            pipeline.addLast("decoder", new HttpRequestDecoder());
            // 添加HTTP响应编码器
            pipeline.addLast("encoder", new HttpResponseEncoder());
            // 添加HTTP响应编码器
            pipeline.addLast("handler", new ReverseProxyHandler(proxyTargetModels));
        }
    }

    // 定义一个内部类，实现反向代理的核心逻辑
    private static class ReverseProxyHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

        private final List<ProxyTargetModel> proxyTargetModels;

        public ReverseProxyHandler(List<ProxyTargetModel> proxyTargetModels) {
            this.proxyTargetModels = proxyTargetModels;
        }

        // 重写channelRead0方法，处理接收到的HTTP请求
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) {
            // 创建一个新的Bootstrap实例，用于连接后端服务器

            String url = req.uri();
            log.debug(url);
            for (ProxyTargetModel proxyTargetModel : proxyTargetModels) {
                Matcher matcher = Pattern.compile(proxyTargetModel.getRegexStr()).matcher(url);
                if (matcher.find()) {
                    Bootstrap b = new Bootstrap();
                    log.debug("找到了匹配项：{}", proxyTargetModel.getRegexStr());
                    b.group(ctx.channel().eventLoop())
                            .channel(ctx.channel().getClass())
                            .handler(new BackendServerHandler(ctx.channel()));

                    // 启动连接后端服务器的尝试，并在连接成功后将HTTP请求转发给后端服务器
                    ChannelFuture f = b.connect(proxyTargetModel.getIp(), proxyTargetModel.getPort());
                    f.addListener((ChannelFutureListener) future -> {
                        if (future.isSuccess()) {
                            Channel inboundChannel = future.channel();
                            // Send the HTTP request to the backend server
                            inboundChannel.writeAndFlush(req).addListener((ChannelFutureListener) writeFuture -> {
                                if (!writeFuture.isSuccess()) {
                                    ctx.close();
                                }
                            });
                        } else {
                            ctx.close();
                        }
                    });
                } else {
                    log.debug("未找到匹配项");
                }

            }
        }
    }

    // 定义一个内部类，处理从后端服务器接收到的HTTP响应
    private static class BackendServerHandler extends SimpleChannelInboundHandler<FullHttpResponse> {

        private final Channel outboundChannel;

        public BackendServerHandler(Channel outboundChannel) {
            this.outboundChannel = outboundChannel;
        }

        // 重写channelRead0方法，处理接收到的HTTP响应，并将其发送回客户端
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, FullHttpResponse msg) {
            // Send the HTTP response back to the client
            outboundChannel.writeAndFlush(msg).addListener((ChannelFutureListener) future -> {
                if (!future.isSuccess()) {
                    ctx.close();
                }
            });
        }
    }
}